package com.tuuzed.hydrogen.internal;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;

import java.util.Arrays;

public final class SQLiteHelper {
    private static final String TAG = "SQLiteHelper";

    public static long insert(@NonNull SQLiteDatabase database,
                              @NonNull String table, String nullColumnHack,
                              @NonNull ContentValues values) {
        try {
            return insertOrThrow(database, table, nullColumnHack, values);
        } catch (SQLException e) {
            Log.e(TAG, "insert: ", e);
            return -1;
        }
    }

    public static long insertOrThrow(@NonNull SQLiteDatabase database,
                                     @NonNull String table,
                                     @Nullable String nullColumnHack,
                                     @NonNull ContentValues values) {
        if (isEchoSQL()) {
            StringBuilder sql = new StringBuilder();
            sql.append("INSERT").append(" INTO ").append(table).append('(');
            int size = values.size();
            if (size > 0) {
                boolean first = true;
                for (String colName : values.keySet()) {
                    sql.append(!first ? "," : "");
                    sql.append(colName);
                    first = false;
                }
                sql.append(") VALUES (");
                for (int i = 0; i < size; i++) {
                    sql.append((i > 0) ? ",?" : "?");
                }
            } else {
                sql.append(nullColumnHack).append(") VALUES (NULL");
            }
            sql.append(')');
            Log.d(TAG, "sql= " + sql);
            Log.d(TAG, "values = " + values.toString());
        }
        return database.insertOrThrow(table, nullColumnHack, values);
    }

    public static int updateOrThrow(@NonNull SQLiteDatabase database,
                                    @NonNull String table,
                                    @NonNull ContentValues values,
                                    @Nullable String whereClause,
                                    @Nullable String[] whereArgs) {
        if (isEchoSQL()) {
            StringBuilder sql = new StringBuilder(120);
            sql.append("UPDATE ");
            sql.append(table);
            sql.append(" SET ");
            boolean first = true;
            for (String colName : values.keySet()) {
                sql.append(!first ? "," : "");
                sql.append(colName);
                sql.append("=?");
                first = false;
            }
            appendClause(sql, " WHERE ", whereClause);
            Log.d(TAG, "sql= " + sql);
            Log.d(TAG, "values= " + values.toString());
            if (whereArgs != null) {
                Log.d(TAG, "whereArgs= " + Arrays.toString(whereArgs));
            }
        }

        return database.update(table, values, whereClause, whereArgs);
    }

    public static int delete(@NonNull SQLiteDatabase database,
                             @NonNull String table,
                             @Nullable String whereClause,
                             @Nullable String[] whereArgs) {
        if (isEchoSQL()) {
            StringBuilder sql = new StringBuilder();
            sql.append("DELETE FROM ").append(table);
            appendClause(sql, " WHERE ", whereClause);
            Log.d(TAG, "sql= " + sql);
            if (whereArgs != null) {
                Log.d(TAG, "whereArgs= " + Arrays.toString(whereArgs));
            }
        }
        return database.delete(table, whereClause, whereArgs);
    }


    public static Cursor query(@NonNull SQLiteDatabase database,
                               boolean distinct,
                               @NonNull String table,
                               @Nullable String[] columns,
                               @Nullable String whereClause,
                               @Nullable String[] whereArgs,
                               @Nullable String groupBy,
                               @Nullable String having,
                               @Nullable String orderBy,
                               @Nullable String limit) {
        if (isEchoSQL()) {
            StringBuilder sql = new StringBuilder(120);
            sql.append("SELECT ");
            if (distinct) sql.append("DISTINCT ");
            if (columns != null && columns.length != 0) appendColumns(sql, columns);
            else sql.append("* ");
            sql.append("FROM ");
            sql.append(table);
            appendClause(sql, " WHERE ", whereClause);
            appendClause(sql, " GROUP BY ", groupBy);
            appendClause(sql, " HAVING ", having);
            appendClause(sql, " ORDER BY ", orderBy);
            appendClause(sql, " LIMIT ", limit);
            Log.d(TAG, "sql= " + sql);
            if (whereArgs != null) {
                Log.d(TAG, "whereArgs= " + Arrays.toString(whereArgs));
            }
        }
        return database.query(distinct, table, columns, whereClause, whereArgs, groupBy, having, orderBy, limit);
    }


    public static void execSQL(@NonNull SQLiteDatabase database,
                               @NonNull String sql) throws SQLException {
        if (isEchoSQL()) {
            Log.d(TAG, "sql= " + sql);
        }
        database.execSQL(sql);
    }

    public static void execSQL(@NonNull SQLiteDatabase database,
                               @NonNull String sql,
                               @NonNull Object[] bindArgs) throws SQLException {
        if (isEchoSQL()) {
            Log.d(TAG, "sql= " + sql);
            Log.d(TAG, "bindArgs= " + Arrays.toString(bindArgs));
        }
        database.execSQL(sql, bindArgs);
    }

    private static void appendColumns(StringBuilder sql, String[] columns) {
        boolean first = true;
        for (String column : columns) {
            if (!first) sql.append(", ");
            sql.append(column);
            first = false;
        }
        sql.append(' ');
    }

    private static void appendClause(StringBuilder sql, String name, String clause) {
        if (!TextUtils.isEmpty(clause)) {
            sql.append(name);
            sql.append(clause);
        }
    }

    private static boolean isEchoSQL() {
        return HydrogenContext.getDefault().isEchoSQL();
    }
}
